﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace DDDB
{
    //封装一个直接落盘系统

    //注意，该系统非线程安
    public class Database : IDisposable
    {
        public Database(string path)
        {
            this.path = path;

            if (System.IO.Directory.Exists(path) == false)
            {
                System.IO.Directory.CreateDirectory(path);
            }
            Console.WriteLine("DDL Start");

            int indexbit = 28;
            int bfbit = 32;
            var file_bf = System.IO.Path.Combine(path, "bf.bin");
            boomfilter = new BoomFilter(bfbit, file_bf);//32bit这个关键值，数据库建立以后就无法修改了
            Console.WriteLine("DDL bf.bin ready");
            var file_index = System.IO.Path.Combine(path, "index.bin");
            index = new PosFilter(indexbit, file_index);
            Console.WriteLine("DDL index.bin ready");

            var range = index.GetFileRange();
            for (ushort i = range.start; i <= range.end; i++)
            {
                datafiles[i] = new DataFile(path, i);
                var file = System.IO.Path.Combine(path, "data_" + i.ToString("X04") + ".bin");
            }
            Console.WriteLine("DDL data_XXXX.bin ready" + " maxid=" + range.end);

        }
        string path;

        BoomFilter? boomfilter;
        PosFilter? index;
        DataFile[] datafiles = new DataFile[256 * 256];
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        DataFile GetDataFile(ushort fileid)
        {
            if (datafiles[fileid] == null)
                datafiles[fileid] = new DataFile(path, fileid);
            return datafiles[fileid];
        }

        public bool Exist(byte[] key, out DataPos pos)
        {
            if (key.Length != 32)
            {
                throw new Exception("must use 256bit key,you can use sha256 to got it.");
            }
            unsafe
            {
                fixed (byte* pkey = key)
                {
                    //第一关 bm 没有
                    uint bindex = boomfilter.GetIndexWithMask(((uint*)pkey)[7]);
                    if (!boomfilter.Get(bindex))
                    {
                        pos = new DataPos();
                        return false;
                    }
                    //第二关 index 没有
                    uint posindex = index.GetIndexWithMask(((uint*)pkey)[0]);
                    pos = index.Get(posindex);
                    if (pos.IsEmpty)
                    {
                        return false;
                    }

                    //可能有
                    while (true)
                    {
                        var head = GetDataFile(pos.datafile).Uncheck_ReadHeadWithKey(pos.datastart);
                        if (head.tag == 1)
                        {
                            if (head.KeyEqual(key))
                            {
                                return true;
                            }
                            else
                            {
                                pos = head.nextpos;
                                if (pos.IsEmpty)
                                    return false;
                                continue;
                            }
                        }
                        else
                        //if (head.tag == 0)
                        {
                            pos = new DataPos();
                            return false;
                        }
                    }

                }

                //如果BF存在，那是可能存在
            }
        }

        public unsafe void Put(byte[] key, byte[] value)
        {
            if (key.Length != 32)
            {
                throw new Exception("must use 256bit key,you can use sha256 to got it.");
            }

            fixed (byte* pkey = key)
            {
                var bmkey = boomfilter.GetIndexWithMask(((uint*)pkey)[7]);
                var poskey = index.GetIndexWithMask(((uint*)pkey)[0]);
                //if(boomfilter.Get(bmkey)==false)
                //{//情况1，不在三界内，写的时候不管

                //}
                var pos = index.Get(poskey);
                if (pos.IsEmpty)
                {
                    //情况2，索引不在表，追写
                    var frange = index.GetFileRange();
                    var datafile = GetDataFile(frange.end);
       
                   bool frangechange = false;


                    while (datafile.WritePos > 0x80ff)//给文件留够位置
                    {
                        frange.end++;
                        frangechange = true;


                        datafile = GetDataFile(frange.end);
                      
                    }
                    pos.datafile = frange.end;
                    pos.datastart = datafile.WritePos;


                    DataHeadWithKey head = new DataHeadWithKey();
                    head.tag = 1;
                    head.SetKey(key);
                    uint filelen = (uint)value.Length;
                    head.len = filelen;
                    var _useblock = DataFile.CalcBlockSize(filelen);
                    head.useblock = _useblock;//最少预留8个
                                              //next =null

                    //写data
                    datafile.Uncheck_WriteHeadWithKeyAndData(pos.datastart, head, value);


                    //写WritePos
                    uint newwritepos = pos.datastart + _useblock;
                    if (newwritepos > 0xffff)
                        newwritepos = 0;
                    if (newwritepos > datafile.WritePos || newwritepos == 0)
                    {
                        datafile.SetWritePos((UInt16)newwritepos);
                    }

                    //写bf
                    boomfilter.Set(bmkey);
                    //写index
                    index.Set(poskey, pos);

                    //写文件范围
                    if (frangechange)
                    {
                        index.SetFileRange(frange);
                    }
                    return;
                }
                else
                {
                    //情况3，索引在表，那就要遍历索引，如果有key则删除
                    var datafile = GetDataFile(pos.datafile);
                    if (datafile.WritePos == 0)
                    {
                        throw new Exception("已封闭");
                    }
                    DataPos posFind = pos;
                    var find = datafile.FindKey(key, ref posFind, out DataHeadWithKey head, out DataPos last, out DataHeadWithKey lasthead);
                    if (find == DataFile.FindKeyState.NotMatch)
                    {
                        DataHeadWithKey whead = new DataHeadWithKey();
                        whead.tag = 1;
                        whead.SetKey(key);
                        uint filelen = (uint)value.Length;
                        whead.len = filelen;
                        var _useblock = DataFile.CalcBlockSize(filelen);
                        whead.useblock = _useblock;
                        whead.nextpos = pos;
                        //预留足够
                        //DataPos newpos = new DataPos { datafile = pos.datafile, datastart = datafile.WritePos };
                        if (datafile.WritePos + _useblock > 0xffff)
                        {
                            throw new Exception("预留不够了");
                        }

                        var writepos = new DataPos { datafile = pos.datafile, datastart = datafile.WritePos };
                        //写data
                        datafile.Uncheck_WriteHeadWithKeyAndData(writepos.datastart, whead, value);

                        //写WritePos
                        uint newwritepos = writepos.datastart + _useblock;
                        if (newwritepos > 0xffff)
                            newwritepos = 0;
                        if (newwritepos > datafile.WritePos || newwritepos == 0)
                        {
                            datafile.SetWritePos((UInt16)newwritepos);
                        }

                        //写bf
                        boomfilter.Set(bmkey);
                        //写index
                        index.Set(poskey, writepos);

                    }
                    else if (find == DataFile.FindKeyState.Match)
                    {//有匹配，那又分为 原地覆盖，和追加覆盖
                        uint filelen = (uint)value.Length;
                        var _useblock = DataFile.CalcBlockSize(filelen);
                        if (head.useblock >= _useblock)
                        {//原地覆盖
                            head.len = filelen;
                            datafile.Uncheck_WriteHeadWithKeyAndData(posFind.datastart, head, value);
                        }
                        else
                        {//追写并删除当前条目
                            DataHeadWithKey whead = new DataHeadWithKey();
                            whead.tag = 1;
                            whead.SetKey(key);       
                            whead.len = filelen;
                            whead.useblock = _useblock;
                            if (posFind.datastart != pos.datastart)
                            {//如果重复项不是第一项，新的索引指向第一项
                                whead.nextpos = pos;
                            }
                            else
                            {//否则就指向下一项
                                whead.nextpos = head.nextpos;
                            }
                            if (datafile.WritePos + _useblock > 0xffff)
                            {
                                throw new Exception("预留不够了");
                            }
                            var writepos = new DataPos { datafile = pos.datafile, datastart = datafile.WritePos };
                            //写data
                            datafile.Uncheck_WriteHeadWithKeyAndData(writepos.datastart, whead, value);

                            //写WritePos
                            uint newwritepos = writepos.datastart + _useblock;
                            if (newwritepos > 0xffff)
                                newwritepos = 0;
                            if (newwritepos > datafile.WritePos || newwritepos == 0)
                            {
                                datafile.SetWritePos((UInt16)newwritepos);
                            }

                         
                            //写bf
                            boomfilter.Set(bmkey);
                            //写index
                            index.Set(poskey, writepos);

                            if (!last.IsEmpty)
                            {//删掉旧的，就是重新链接一下
                                lasthead.nextpos = head.nextpos;
                                datafile.Uncheck_WriteHeadWithKey(last.datastart, lasthead);
                            }
                            {
                                head.tag = 2;//原来的标记为删除
                                datafile.Uncheck_WriteHeadWithKey(pos.datastart, head);
                            }
                            
                        }
                    }
                }

            }
        }
        public byte[] Get(byte[] key)
        {
            if (key.Length != 32)
            {
                throw new Exception("must use 256bit key,you can use sha256 to got it.");
            }
            if (Exist(key, out DataPos pos))
            {
                return GetByPos(pos);
            }
            else
            {
                return null;
            }
        }
        public byte[] GetByPos(DataPos pos)
        {
            var file = GetDataFile(pos.datafile);
            file.Uncheck_ReadHeadWithKeyAndData(pos.datastart, out byte[] data);
            return data;
        }

        public void Dispose()
        {
            index.Dispose();
            boomfilter.Dispose();
            index = null;
            boomfilter = null;


            foreach (var fd in datafiles)
            {
                if (fd != null)
                    fd.Dispose();
            }
            datafiles = null;

        }
    }
}
